// 定义一个蛇的类
class Snake {
	// 表示蛇头的元素
	private head: HTMLElement;
	// 表示蛇身体(包括蛇头)的元素
	// 使用泛型，指定HTMLCollection里面的元素是HTMLElement
	private bodies: HTMLCollectionOf<HTMLElement>;
	// 蛇的容器
	private snakeEle: HTMLElement;

	constructor() {
		this.snakeEle = document.getElementById('snake')!;
		this.head = document.querySelector('#snake > div')!;
		this.bodies = this.snakeEle.getElementsByTagName('div');
	}

	// 获取蛇的坐标（蛇头坐标）
	get X(): number {
		return this.head.offsetLeft;
	}

	get Y(): number {
		return this.head.offsetTop;
	}

	// 设置蛇头的坐标
	set X(value: number) {
		// 如果新值和旧值相同，则直接返回不再修改
		if (this.X === value) {
			return;
		}

		// X的值的合法范围0-290之间
		if (value < 0 || value > 290) {
			// 进入判断说明撞墙
			throw new Error('蛇撞墙了 GAME OVER!');
		}

		// 蛇至少有两节时，修改X时，是在修改水平坐标，蛇在左右移动，蛇在向左移动时，不能向右掉头，反之亦然
		// 当蛇第二节存在，且蛇头的新值与第二节的坐标相等，说明掉头了
		if (this.bodies[1] && this.bodies[1].offsetLeft == value) {
			if (value > this.X) {
				// 如果新值value大于旧值X，则说明蛇在向左走，蛇头向右，此时发生掉头，应该使蛇头继续向左走
				value = this.X - 10;
			} else {
				value = this.X + 10;
			}
		}

		// 在蛇头移动前，先移动身体
		this.moveBody();
		this.head.style.left = value + 'px';
		// 蛇头移动后，判断是否撞到身体
		this.checkHitBody();
	}

	set Y(value: number) {
		// 如果新值和旧值相同，则直接返回不再修改
		if (this.Y === value) {
			return;
		}

		// Y的值的合法范围0-290之间
		if (value < 0 || value > 290) {
			// 进入判断说明撞墙
			throw new Error('蛇撞墙了 GAME OVER!');
		}

		// 蛇至少有两节时，修改Y时，是在修改垂直坐标，蛇在左右移动，蛇在向左移动时，不能向右掉头，反之亦然
		// 当蛇第二节存在，且蛇头的新值与第二节的坐标相等，说明掉头了
		if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop == value) {
			if (value > this.Y) {
				// 如果新值value大于旧值X，则说明蛇在向左走，蛇头向右，此时发生掉头，应该使蛇头继续向左走
				value = this.Y - 10;
			} else {
				value = this.Y + 10;
			}
		}

		// 在蛇头移动前，先移动身体
		this.moveBody();
		this.head.style.top = value + 'px';
		// 蛇头移动后，判断是否撞到身体
		this.checkHitBody();
	}

	// 蛇增加身体的方法
	addBody() {
		// 向snakeEle中添加一个div
		this.snakeEle.appendChild(document.createElement('div'));
	}

	// 添加一个蛇身体移动的方法
	moveBody() {
		/*
		 *   将后边的身体设置为前边身体的位置
		 *       举例子：
		 *           第4节 = 第3节的位置
		 *           第3节 = 第2节的位置
		 *           第2节 = 蛇头的位置
		 */
		// 遍历蛇的身体，从后往前遍历
		for (let i = this.bodies.length - 1; i > 0; i--) {
			// 获取前边身体的位置
			let x = this.bodies[i - 1].offsetLeft;
			let y = this.bodies[i - 1].offsetTop;
			// 将值设置到当前身体上
			this.bodies[i].style.left = x + 'px';
			this.bodies[i].style.top = y + 'px';
		}
	}

	// 判断蛇是否撞到身体
	checkHitBody() {
		// 身体大于4节才能撞到身体
		if (this.bodies.length > 4) {
			for (let i = 1; i < this.bodies.length; i++) {
				if (this.X == this.bodies[i].offsetLeft && this.Y == this.bodies[i].offsetTop) {
					throw new Error('撞到自己了 GAME OVER!');
				}
			}
		}
	}
}

export default Snake;
